const { RawSource }  = require("webpack-sources");
const path = require("path");
const Spritesmith = require('spritesmith');
const fs = require('fs-extra')
const { store } = require("./store");

class vueSpritePlugin {

    constructor(data){
        this.dir = data.dir || "img";
        this.filename = data.filename || "css-sprite.png";
        this.exclude = data.exclude || [];
    }

    excludeHandler(filename){
        const result = this.exclude.find((name)=>{
            return filename.includes(name);
        })
        if(result){
            return false; // 被排除的文件不处理
        }else{
            return true;
        }
    }

    imgHandler(imgMap,compilation){

        const dir = path.join(__dirname,'img-sprite-tmp');

        fs.ensureDirSync(dir);//创建临时文件夹

        const sprites = [];

        for(let [key,value] of imgMap){
            const filename = path.join(dir,key);
            fs.outputFileSync(filename,value.source());
            sprites.push(filename);
        }

        return new Promise((resolve,reject)=>{

            Spritesmith.run({src: sprites},(err, result)=>{

                if(err){
                    reject(err);
                }

                const spriteFile = path.relative(this.outputPath,this.spriteFile); // 得到图片的相对路径
      
                compilation.assets[spriteFile] = {
                    source(){
                        return result.image;
                    },
                    size(){
                        return result.image.length;
                    }
                };
      
                // 删除缓存文件夹
                fs.removeSync(dir);
      
                for(let key in result.coordinates){
                    const value = result.coordinates[key];
                    const filename = path.basename(key);
                    if(imgMap.has(filename)){
                      imgMap.set(filename,value);
                    }
                }

                resolve(null);
      
              });

        })

    }

    // 修改css路径
    updateCssPath(imgMap,fileSet,compilation){
        const spriteFile = path.relative(path.join(this.outputPath,"/css"),this.spriteFile); // 得到图片的相对路径
        fileSet.forEach((filename)=>{
            let content = compilation.assets[filename].source(); // css文件内容
            const reg = /background-image\s*:\s*url\((['"])?([^'"{};]+)(['"])?\)/g;
            content = content.replace(reg,(matched,$1,$2,$3)=>{
                const filename = path.basename($2);
                if(!imgMap.has(filename)){
                    return matched;
                }
                const value = imgMap.get(filename);
                return `background-image:url('${spriteFile.replace(/\\/g,"\/")}');background-repeat:no-repeat;background-position:-${value.x}px -${value.y}px;`;
            })
            compilation.updateAsset(filename,(old)=>{
                return new RawSource(content,filename);
            })
        })
    }

    // 该图片是否要处理
    needHandler(url){  
        for(let value of store.value){
            const { name,ext } = path.parse(value);
            if(url.startsWith(name) && url.endsWith(ext)){
                return true;
            }
        }
        return false;
    }
   
    apply(compiler){

        compiler.hooks.emit.tapAsync('vueSpritePlugin',(compilation,callback)=>{

            this.outputPath = compilation.options.output.path; //导出文件夹的根目录

            this.spriteFile = path.join(this.outputPath,this.dir,this.filename); // 生成的雪碧图路径

            const keys = Object.keys(compilation.assets);

            const list = keys.filter((item)=>{
                const reg = new RegExp("\.css$");
                if(reg.test(item.trim()) && this.excludeHandler(item)){
                    return true;
                }else{
                    return false;
                }
            })
            
            const imgs = new Map(); // 所有图片资源存一下

            keys.forEach((item)=>{
                const reg = new RegExp("\.(png|jpg|jpeg)$");
                if(reg.test(item.trim())){
                    const filename = path.basename(item.trim());
                    imgs.set(filename,compilation.assets[item])
                }
            })

            const fileSet = new Set(); // 存一下待处理的文件路径

            const imgMap = new Map(); // 图片路径映射

            const tmpJson = "./spriteStore.json";

            if(store.value.size > 0){ //有值
                 fs.outputJsonSync(tmpJson, {
                     data: Array.from(store.value)
                 })
            }else{
                try {
                    const result = fs.readJsonSync(tmpJson);
                    if(result && result.data){
                        store.value = new Set(result.data);
                    }   
                } catch (error) {
                    
                }
            }

            // 对所有css文件做正则判断,取出图片路径 background-img:url('') no-repeat;
            list.forEach((filename)=>{
                 const source = compilation.assets[filename].source(); // 拿到css源码
                 const reg = /background-image\s*:\s*url\((['"])?([^'"{};]+)(['"])?\)/g;
                 let result;
                 while(result = reg.exec(source)){
                     const url = result[2]; // 获取到图片名
                     const new_url = path.basename(url.trim());
                     if(store.value && this.needHandler(new_url)){
                        fileSet.add(filename);
                        if(imgs.has(new_url)){
                           imgMap.set(new_url,imgs.get(new_url))
                        } 
                     }
                 }   
            })

            store.value.clear();

            this.imgHandler(imgMap,compilation).then(()=>{
                this.updateCssPath(imgMap,fileSet,compilation);
                callback();
            })  
        })
        
    }

}

module.exports = vueSpritePlugin;

